﻿// Partial Copyright (c) LogoUI Software Solutions LTD
// Autor: Vladislav Spivak
// This source file is the part of LogoFX Framework http://logofx.codeplex.com
// See accompanying licences and credits.

//this functionality is replacement for sharedsizegroup in wpf
//based on, but not limited to http://databaseconsultinggroup.com/blog/2009/05/simulating_sharedsizegroup_in.html

#if SILVERLIGHT

using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;

namespace LogoFX.Controls
{
    
	/// <summary> Content container control which sizes it's width and/or height to a group. </summary>
	public class SharedSizePanel : ContentPresenter
	{
		public static readonly DependencyProperty WidthGroupProperty = DependencyProperty.Register("WidthGroup", typeof(SharedSizeGroup), typeof(SharedSizePanel), new PropertyMetadata(null, new PropertyChangedCallback(WidthGroupChanged)));

		/// <summary> Specifies the shared size group with which to synchronize the width. </summary>
		public SharedSizeGroup WidthGroup
		{
			get { return (SharedSizeGroup)GetValue(WidthGroupProperty); }
			set { SetValue(WidthGroupProperty, value); }
		}

		/// <summary> Handles WidthGroup changes by unregistering from the old group and registering with the new. </summary>
		private static void WidthGroupChanged(DependencyObject ASender, DependencyPropertyChangedEventArgs AArgs)
		{
			SharedSizePanel LThis = (SharedSizePanel)ASender;

			SharedSizeGroup LGroup = (SharedSizeGroup)AArgs.OldValue;
			if (LGroup != null && LThis.HeightGroup != LGroup)
				LGroup.UnregisterPanel(LThis);

			LGroup = (SharedSizeGroup)AArgs.NewValue;
			if (LGroup != null)
				LGroup.RegisterPanel(LThis);
		}

		public static readonly DependencyProperty HeightGroupProperty = DependencyProperty.Register("HeightGroup", typeof(SharedSizeGroup), typeof(SharedSizePanel), new PropertyMetadata(null, new PropertyChangedCallback(HeightGroupChanged)));

		/// <summary> Specifies the shared size group with which to synchronize the height. </summary>
		public SharedSizeGroup HeightGroup
		{
			get { return (SharedSizeGroup)GetValue(HeightGroupProperty); }
			set { SetValue(HeightGroupProperty, value); }
		}

		/// <summary> Handles HeightGroup changes by unregistering from the old group and registering with the new. </summary>
		private static void HeightGroupChanged(DependencyObject ASender, DependencyPropertyChangedEventArgs AArgs)
		{
			SharedSizePanel LThis = (SharedSizePanel)ASender;

			SharedSizeGroup LGroup = (SharedSizeGroup)AArgs.OldValue;
			if (LGroup != null && LThis.WidthGroup != LGroup)
				LGroup.UnregisterPanel(LThis);

			LGroup = (SharedSizeGroup)AArgs.NewValue;
			if (LGroup != null)
				LGroup.RegisterPanel(LThis);
		}

		/// <summary> Performs measurement utilizing a shared width and/or height. </summary>
		protected override Size MeasureOverride(Size AAvailableSize)
		{
			if (VisualTreeHelper.GetChildrenCount(this) >= 0)
			{
				// Measure the child - not as simple as acccessing the Content as that is not our true Child in the visual tree
				UIElement LChild = (UIElement)VisualTreeHelper.GetChild(this, 0);
				LChild.Measure(AAvailableSize);
				Size LChildSize = LChild.DesiredSize;
				
				// Measure using the group for width and/or height
				Size LShared = new Size(0, 0);
				if (WidthGroup != null)
					LShared = WidthGroup.Measure(this, LChildSize);
				if (HeightGroup != null)
				{
					if (HeightGroup != WidthGroup)
						LShared.Height = HeightGroup.Measure(this, LChildSize).Height;
				}
				else
					LShared.Height = 0;
				
				// Return the greater of the child and the group's dimensions
				return new Size(Math.Max(LChildSize.Width, LShared.Width), Math.Max(LChildSize.Height, LShared.Height));
			}
			else
				return base.MeasureOverride(AAvailableSize);
		}
	}

    public class SharedSizeGroup
    {
        /// <summary> 
        /// Internal set of panels which are subscribed to this groups, with their
        /// last measures sizes. 
        /// </summary>
        private Dictionary<SharedSizePanel, Size> FPanels = new Dictionary<SharedSizePanel, Size>();

        /// <summary> 
        /// Registers the given panel with this group. 
        /// </summary>
        /// <remarks> 
        /// Nothing is done if the panel is already registered.  If newly
        /// registered, this and all other panels in the group will be re-measured. 
        /// </remarks>
        public void RegisterPanel(SharedSizePanel AElement)
        {
            if (!FPanels.ContainsKey(AElement))
            {
                FPanels.Add(AElement, new Size(0, 0));

                InvalidateAllMeasure();
            }
        }

        /// <summary> Unregisters the given panel from this group. </summary>
        /// <remarks> All panels in the group will be re-measured. </remarks>
        public void UnregisterPanel(SharedSizePanel APanel)
        {
            // Invalidate all measurements (first to ensure that the unregistered panel is included)
            InvalidateAllMeasure();

            // Removes the panel if it is registered
            FPanels.Remove(APanel);
        }

        /// <summary> Used internally to invalidate all registered panels. </summary>
        private void InvalidateAllMeasure()
        {
            foreach (KeyValuePair<SharedSizePanel, Size> LEntry in FPanels)
                LEntry.Key.InvalidateMeasure();
        }

        /// <summary> Internally cached measured size. </summary>
        private Size FSize = new Size(0, 0);

        /// <summary> Measures the current group in reaction to a measurement of the given panel. </summary>
        /// <remarks> This method is optimized to only re-measure the group sizes if the given 
        /// change appears possible to change the measurement. </remarks>
        /// <param name="APanel"> The panel that is being measured. </param>
        /// <param name="AMeasured"> The (new) content size of the given panel. </param>
        /// <returns> The size of the group. </returns>
        internal Size Measure(SharedSizePanel APanel, Size AMeasured)
        {
            System.Diagnostics.Debug.Assert(FPanels.ContainsKey(APanel), "Attempt to measure by unregistered panel.");
            Size LOldSize = FPanels[APanel];

            if
            (
              LOldSize != AMeasured
                &&
                (
                  APanel.WidthGroup != this
                    || LOldSize.Width >= FSize.Width
                    || APanel.HeightGroup != this
                    || LOldSize.Height >= FSize.Height
                )
            )
            {
                // Set the measured size for the given panel
                FPanels[APanel] = AMeasured;

                // Measure the group
                FSize = new Size(0, 0);
                foreach (KeyValuePair<SharedSizePanel, Size> LEntry in FPanels)
                {
                    if ((LEntry.Key.WidthGroup == this) && (LEntry.Value.Width > FSize.Width))
                        FSize.Width = LEntry.Value.Width;
                    if ((LEntry.Key.HeightGroup == this) && (LEntry.Value.Height > FSize.Height))
                        FSize.Height = LEntry.Value.Height;
                }

                // Invalidate the measurement of all but the given panel
                foreach (KeyValuePair<SharedSizePanel, Size> LEntry in FPanels)
                    if (LEntry.Key != APanel)
                        LEntry.Key.InvalidateMeasure();
            }
            return FSize;
        }
    }
}
#endif